package veapplib

import (
	_ "embed"
	"errors"
	"fmt"
	"strings"
	"syscall"
)

type UserDll struct {
	fnAddUser              *syscall.LazyProc
	fnAddUserPlus          *syscall.LazyProc //添加用户并绑定用户组
	fnDelUser              *syscall.LazyProc
	fnAddGroup             *syscall.LazyProc
	fnDelGroup             *syscall.LazyProc
	fnAddUserToGroup       *syscall.LazyProc
	fnDelUserToGroup       *syscall.LazyProc
	fnGetGroupList         *syscall.LazyProc
	fnGetGroupUsers        *syscall.LazyProc
	fnGetUserGroups        *syscall.LazyProc
	fnSetUserActive        *syscall.LazyProc
	fnSetUserPassword      *syscall.LazyProc
	fnSetUserPasswordAdmin *syscall.LazyProc
	fnReturnErrInfo        *syscall.LazyProc
	fnModifyDir            *syscall.LazyProc
	fnGetDoMainName        *syscall.LazyProc
}

func NewUserDll() *UserDll {
	handle := syscall.NewLazyDLL("./UserManager.dll")
	return &UserDll{
		fnAddUser:              handle.NewProc("AddUser"),              //添加用户
		fnDelUser:              handle.NewProc("DelUser"),              //删除用户
		fnAddGroup:             handle.NewProc("AddGroup"),             //创建用户组
		fnDelGroup:             handle.NewProc("DelGroup"),             //删除用户组
		fnAddUserToGroup:       handle.NewProc("AddUserToGroup"),       //将用户加入组
		fnDelUserToGroup:       handle.NewProc("DelUserToGroup"),       //将用户从用户组中移除
		fnGetGroupList:         handle.NewProc("GetGroupList"),         //获取用户组列表
		fnGetGroupUsers:        handle.NewProc("GetGroupUsers"),        //获取指定用户组中的所有用户列表
		fnGetUserGroups:        handle.NewProc("GetUserGroups"),        //获取用户所属的用户组
		fnSetUserActive:        handle.NewProc("SetUserActive"),        //禁用或启用用户
		fnSetUserPassword:      handle.NewProc("SetUserPassword"),      //用户设置密码：需原密码
		fnSetUserPasswordAdmin: handle.NewProc("SetUserPasswordAdmin"), //重置用户密码：无需原密码
		fnAddUserPlus:          handle.NewProc("AddUserPlus"),          //添加用户并分配用户组
		fnReturnErrInfo:        handle.NewProc("ReturnErrInfo"),        //获取错误信息
		fnModifyDir:            handle.NewProc("ModifyDir"),            //修改用户磁盘配额
		fnGetDoMainName:        handle.NewProc("GetDoMainName"),        //修改用户磁盘配额
	}
}

func (s *UserDll) GetGroupUsers(group string) (string, error) {
	var buf = make([]byte, 65536)
	var bufsize uint32 = 0
	r1, r2, err := s.fnGetGroupUsers.Call(AStrPtr(group), newBuffer(buf), newBufferSize(&bufsize)) //nolint
	if err != nil {
		return "", errors.New(s.ReturnErrInfo(int32(r1)))
	}
	fmt.Println(r1, r2, err)
	return string(buf[:bufsize]), nil
}

func (s *UserDll) AddUser(username, password string) (int32, error) {
	code, _, _ := s.fnAddUser.Call(AStrPtr(username), AStrPtr(password)) //nolint
	if code != 0 {
		return int32(code), errors.New(s.ReturnErrInfo(int32(code)))
	}
	return 0, nil
}

func (s *UserDll) AddUserToGroup(user, group string) (int32, error) {
	code, _, _ := s.fnAddUserToGroup.Call(AStrPtr(group), AStrPtr(user)) //nolint
	if code != 0 {
		return int32(code), errors.New(s.ReturnErrInfo(int32(code)))
	}
	return 0, nil
}

func (s *UserDll) SetUserDiskLimit(username string, limit int32) (int32, error) {
	code, _, _ := s.fnModifyDir.Call(AStrPtr(username), IntPtr(limit)) //nolint
	if code != 0 {
		return int32(code), errors.New(s.ReturnErrInfo(int32(code)))
	}
	return 0, nil
}

// SetUserPassword 用户密码设置：需要验证原密码
func (s *UserDll) SetUserPassword(user, password, npassword string) (int32, error) {
	code, _, _ := s.fnSetUserPassword.Call(AStrPtr(user), AStrPtr(password), AStrPtr(npassword)) //nolint
	if code != 0 {
		return int32(code), errors.New(s.ReturnErrInfo(int32(code)))
	}
	return 0, nil
}

// SetUserPasswordAdmin 用户密码设置：无需验证原密码，管理员强制重置
func (s *UserDll) SetUserPasswordAdmin(username, password string) (int32, error) {
	code, _, _ := s.fnSetUserPasswordAdmin.Call(AStrPtr(username), AStrPtr(password)) //nolint
	if code != 0 {
		return int32(code), errors.New(s.ReturnErrInfo(int32(code)))
	}
	return 0, nil
}

func (s *UserDll) ReturnErrInfo(code int32) (msg string) {
	var buf = make([]byte, 65536)
	var bufsize uint32 = 0
	scode, _, _ := s.fnReturnErrInfo.Call(IntPtr(code), newBuffer(buf), newBufferSize(&bufsize)) //nolint
	if scode == 0 {
		outText := strings.Trim(string(buf[:bufsize]), "\r\n")
		return outText
	}
	return fmt.Sprintf("未知错误状态码：%d", code)
}
